package models

import (
	"bufio"
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/disk"
	"github.com/shirou/gopsutil/mem"
	"github.com/shirou/gopsutil/process"
	"github.com/zeromicro/go-zero/core/logx"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

/**
 * @Author: way lee
 * @Date: 2024/9/23 16:29
 * @Desc:
 */

// HeartbeatInfo 表示心跳信息结构
type HeartbeatInfo struct {
	ClusterId     string  `db:"cluster_id" json:"clusterID"`
	AgentId       string  `db:"agent_id" json:"agentID"`
	HostName      string  `db:"hostname" json:"hostName"`
	IPAddress     string  `db:"ip_address" json:"ipAddress"`
	Status        bool    `db:"status" json:"status"`
	CPUUsage      float64 `db:"cpu_usage" json:"cpuUsage,omitempty"`
	MemoryUsage   float64 `db:"memory_usage" json:"memoryUsage,omitempty"`
	DiskSpace     float64 `db:"disk_space" json:"diskSpace,omitempty"`
	ProcessCount  int     `db:"process_count" json:"processCount,omitempty"`
	LastHeartbeat string  `db:"last_heartbeat" json:"lastHeartbeat,omitempty"`
}

// AddHeartbeatsRequest 用于发送心跳信息的请求结构
type AddHeartbeatsRequest struct {
	HeartbeatInfo HeartbeatInfo `json:"heartbeatInfo"`
}

// SendHeartbeat 发送心跳信息到指定的URL
func SendHeartbeat(ctx context.Context, url string, heartbeatInfo HeartbeatInfo) error {
	reqData := AddHeartbeatsRequest{HeartbeatInfo: heartbeatInfo}
	jsonData, err := json.Marshal(reqData)
	if err != nil {
		logx.WithContext(ctx).Errorf("JSON序列化心跳信息失败: %v", err)
		return fmt.Errorf("failed to marshal heartbeat info: %w", err)
	}

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("http://%s/v1/log/add_heartbeats", url), bytes.NewBuffer(jsonData))
	if err != nil {
		logx.WithContext(ctx).Errorf("创建HTTP请求失败: %v", err)
		return fmt.Errorf("failed to create HTTP request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		logx.WithContext(ctx).Errorf("发送HTTP请求失败: %v", err)
		return fmt.Errorf("failed to send HTTP request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		logx.WithContext(ctx).Errorf("HTTP请求返回非预期状态码: %d", resp.StatusCode)
		return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
	}

	return nil
}

// GetLocalIP 获取本地非回环地址的 IP
func GetLocalIP() (string, error) {
	interfaces, err := net.Interfaces()
	if err != nil {
		return "", fmt.Errorf("获取网络接口错误: %w", err)
	}

	for _, iface := range interfaces {
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			continue
		}

		addrs, err := iface.Addrs()
		if err != nil {
			return "", fmt.Errorf("获取接口 %s 的地址错误: %w", iface.Name, err)
		}

		for _, addr := range addrs {
			ipNet, isValidIPNet := addr.(*net.IPNet)
			if isValidIPNet && !ipNet.IP.IsLoopback() && ipNet.IP.To4() != nil {
				return ipNet.IP.String(), nil
			}
		}
	}

	return "", fmt.Errorf("未找到有效的IP地址")
}

// GetHeartbeatInfo 获取心跳信息
func GetHeartbeatInfo() (HeartbeatInfo, error) {
	ip, err := GetLocalIP()
	if err != nil {
		return HeartbeatInfo{}, fmt.Errorf("获取IP错误: %w", err)
	}

	hostname, err := os.Hostname()
	if err != nil {
		return HeartbeatInfo{}, fmt.Errorf("获取主机名错误: %w", err)
	}

	cpuUsage, memUsage, diskUsage, processCount, err := GetResourceUsage()
	if err != nil {
		return HeartbeatInfo{}, fmt.Errorf("获取资源使用情况错误: %w", err)
	}

	exePath, err := os.Executable()
	if err != nil {
		return HeartbeatInfo{}, fmt.Errorf("获取可执行路径错误: %w", err)
	}

	parentDir := filepath.Dir(filepath.Dir(exePath))
	tmpDir := filepath.Join(parentDir, "tmp")
	clusterFilePath := filepath.Join(tmpDir, "CLUSTER")

	clusterID, err := ReadClusterID(clusterFilePath)
	if err != nil {
		return HeartbeatInfo{}, err
	}

	return HeartbeatInfo{
		ClusterId:     clusterID,
		AgentId:       "agent_" + ip,
		HostName:      hostname,
		IPAddress:     ip,
		Status:        true,
		CPUUsage:      cpuUsage,
		MemoryUsage:   memUsage,
		DiskSpace:     diskUsage,
		ProcessCount:  processCount,
		LastHeartbeat: time.Now().Format("2006-01-02 15:04:05.000"),
	}, nil
}

// ReadClusterID 读取CLUSTER文件中的集群ID
func ReadClusterID(filePath string) (string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return "", fmt.Errorf("打开CLUSTER文件错误: %w", err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	if scanner.Scan() {
		return strings.TrimSpace(scanner.Text()), nil
	}

	if err = scanner.Err(); err != nil {
		return "", fmt.Errorf("读取CLUSTER文件错误: %w", err)
	}

	return "", fmt.Errorf("CLUSTER文件为空")
}

// formatFloat 格式化浮点数为两位小数
func formatFloat(value float64) float64 {
	strValue := fmt.Sprintf("%.2f", value)
	formattedValue, _ := strconv.ParseFloat(strValue, 64)
	return formattedValue
}

// GetResourceUsage 获取CPU、内存、磁盘使用情况和进程数
func GetResourceUsage() (float64, float64, float64, int, error) {
	cpuUsage, err := getCPUUsage()
	if err != nil {
		return 0, 0, 0, 0, err
	}

	memUsage, err := getMemoryUsage()
	if err != nil {
		return 0, 0, 0, 0, err
	}

	diskUsage, err := getDiskUsage()
	if err != nil {
		return 0, 0, 0, 0, err
	}

	processCount, err := getProcessCount()
	if err != nil {
		return 0, 0, 0, 0, err
	}

	return cpuUsage, memUsage, diskUsage, processCount, nil
}

// 获取CPU使用率
func getCPUUsage() (float64, error) {
	cpuPercent, err := cpu.Percent(0, false)
	if err != nil {
		logx.Errorf("获取CPU使用率错误: %v", err)
		return 0, err
	}
	return formatFloat(cpuPercent[0]), nil
}

// 获取内存使用率
func getMemoryUsage() (float64, error) {
	vmStat, err := mem.VirtualMemory()
	if err != nil {
		logx.Errorf("获取内存使用率错误: %v", err)
		return 0, err
	}
	return formatFloat(vmStat.UsedPercent), nil
}

// 获取磁盘使用率
func getDiskUsage() (float64, error) {
	diskStat, err := disk.Usage("/")
	if err != nil {
		logx.Errorf("获取磁盘使用率错误: %v", err)
		return 0, err
	}
	return formatFloat(diskStat.UsedPercent), nil
}

// 获取进程总数
func getProcessCount() (int, error) {
	processes, err := process.Pids()
	if err != nil {
		logx.Errorf("获取进程数错误: %v", err)
		return 0, err
	}
	return len(processes), nil
}
